;; The first four lines of this file were added by Dracula.
;; They tell DrScheme that this is a Dracula Modular ACL2 program.
;; Leave these lines unchanged so that DrScheme can properly load this file.
#reader(planet "reader.rkt" ("cce" "dracula.plt") "modular" "lang")
(require "imain.lisp")

(require "mcommon.lisp")
(require "mfilereader.lisp")
(require "mimage.lisp")
(require "mcolorize.lisp")
(require "mFlip.lisp")
(require "mthreshold.lisp")
(require "mcrop.lisp")
(require "mtrim.lisp")
(require "mresize.lisp")
(require "mrotate.lisp")
(require "mdecorate.lisp")
(require "mgenerate.lisp")
(require "moverlay.lisp")
(require "mdraw.lisp")
(require "mgrayscale.lisp")
(require "mcolorthreshold.lisp")
(require "mhipstafy.lisp")
(require "mfisheye.lisp")
(require "mhuechange.lisp")
(require "msharpen.lisp")
(require "mmask.lisp")
(require "mnegative.lisp")

(module MMain
  (import ICommon)
  (import IFileReader)
  (import IImage)
  (import IColorize)
  (import IFlip)
  (import IThreshold)
  (import ICrop)
  (import ITrim)
  (import IResize)
  (import IRotate)
  (import IDecorate)
  (import IGenerate)
  (import IOverlay)
  (import IDraw)
  (import IBlur)
  (import IGrayscale)
  (import IColorThreshold)
  (import IHipstafy)
  (import IFisheye)
  (import IHueChange)
  (import ISharpen)
  (import INegative)
  (import IMask)
  
  (include-book "binary-io-utilities" :dir :teachpacks)
  (include-book "list-utilities" :dir :teachpacks)
  (include-book "io-utilities" :dir :teachpacks)
  (include-book "avl-rational-keys" :dir :teachpacks)

  
  
  ;For testing a single image operation alone
  (defun testImageFunction (inputBMP outputBMP state)
    (mv-let (bmp-bytes err-opn-bmp state)
            (binary-file->byte-list inputBMP state)
            (if err-opn-bmp
                (mv err-opn-bmp state)
                (byte-list->binary-file
                 outputBMP
                 ;(princ$ (avl-retrieve (bmp->tree bmp-bytes) -1))
                 ;(tree->bmp (car (MainTrim (bmp->tree bmp-bytes) nil)))
                 ; (princ$ (avl-delete (bmp->tree bmp-bytes) -1))
                 ;(princ$ (MainCrop (avl-delete (bmp->tree bmp-bytes) -1) (stringToArgs "2 2 3 3")))
                 ;(tree->bmp (MainColorize (bmp->tree bmp-bytes) 3 3 3 "MUL"))
                 ;(tree->bmp (MainColorize (bmp->tree bmp-bytes) 100 100 100 "ADD"))
                 ;(tree->bmp (MainColorize (bmp->tree bmp-bytes) 2 1 1 "MUL"))
                 ;(tree->bmp (MainColorize (bmp->tree bmp-bytes) 100 0 0 "ADD"))
                 ;(tree->bmp (MainColorize (bmp->tree bmp-bytes) 1 2 2 "MUL"))
                 ;(tree->bmp (MainColorize (bmp->tree bmp-bytes) 0 100 100 "ADD"))
                 ;(tree->bmp(flip-h (bmp->tree bmp-bytes)))
                 ;(flip-h (bmp->tree bmp-bytes))
                 ;(tree->bmp (flip-v (bmp->tree bmp-bytes)))
                 (tree->bmp (MainThreshold (bmp->tree bmp-bytes) (list 100 100 100))) 
                 ;(tree->bmp (MainDecorate (bmp->tree bmp-bytes) (list 16 128 32 32)))
                 ;(tree->bmp (MainBlur (bmp->tree bmp-bytes) ()))
                 state))))
  
  ;Easy accessor for testImageFunction
  (defun run ()
    (testImageFunction "4x4.bmp" "out.bmp" STATE))
  
  
  ;Checks if the input (as a list of chars) contains the EndFile marker
  (defun containsEndFile (inputasChars)
    (let* ((breakPair (breakAtNextLine inputAsChars)))
      (let* ((line (car breakPair))
             (rest (cadr breakPair)))
        (if (string-equal (chrs->str line) "EndFile")
            1
            (if (equal rest nil)
                nil
                (containsEndFile rest))))))
  
 
  ;Gets a list of commands (each a list of chars) up until the EndFile marker
  (defun getFileCommands (inputAsChars)
    (let* ((breakPair (breakAtNextLine inputAsChars)))
      (let* ((line (car breakPair))
             (rest (cadr breakPair)))
        (if (string-equal (chrs->str line) "EndFile")
            nil
            (append (list line) (getFileCommands rest))))))
  
  (defun getRemainingFiles(inputAsChars)
    (let* ((breakPair (breakAtNextLine inputAsChars)))
      (let* ((line (car breakPair))
             (rest (cadr breakPair)))
        (if (or (equal line nil) (string-equal (chrs->str line) "EndFile"))
            rest
            (getRemainingFiles rest)))))
            
  ;Takes in a list of arguements (first is type) and a tree.
  ;Returns a mv pair (tree, errors)
  (defun runCommand (arguments tree errors)
    (let* ((type (car arguments))
           (args (cdr arguments)))
      (mv-let (newTree newErrors)
              (cond ;Each Main Command should take in a tree and a list of args. It should return a MV pair (tree, errors).
                ((string-equal type "COLORIZE") (MainColorize tree args))
                ((string-equal type "THRESHOLD") (MainThreshold tree args))
                ((string-equal type "COLORTHRESHOLD") (MainColorThreshold tree args))
                ((string-equal type "FLIP") (MainFlip tree args))
                ((string-equal type "RESIZE") (MainResize tree args))
                ((string-equal type "CROP") (MainCrop tree args))
                ((string-equal type "TRIM") (MainTrim tree args))
                ((string-equal type "ROTATE") (MainRotate tree args))
                ((string-equal type "DRAW") (MainDraw tree args))
                ((string-equal type "DECORATE") (MainDecorate tree args))
                ((string-equal type "GENERATE") (MainGenerate tree args))
                ((string-equal type "OVERLAY") (MainOverlay tree args))
                ((string-equal type "BLUR") (MainBlur tree args))
                ((string-equal type "GRAYSCALE") (MainGrayscale tree))
                ((string-equal type "HIPSTAFY") (MainHipstafy tree))
                ((string-equal type "FISHEYE") (MainFisheye tree))
                ((string-equal type "NEGATIVE") (MainNegative tree))
                ((string-equal type "SHARPEN") (MainSharpen tree args))
                ((string-equal type "HUECHANGE") (MainHueChange tree args))
                ((string-equal type "MASK") (MainMask tree args)))
                
              (mv newTree (append errors newerrors)))))
  
  ;Takes in a list of commands and tree and executes them all one at a time.
  ;Returns a mv pair (tree, errors)
  (defun runAllCommands (commands tree errors)
    (if (consp commands)
        (mv-let (newTree newErrors)
                (time$ (runCommand (stringToArgs (chrs->str (car commands))) tree errors))
                (runAllCommands (cdr commands) newTree newErrors))
        (mv tree errors)))
  
  (defun generateError(errors)
    (if (consp errors)
        errors
        (list "Image Manipulation Completed Without Errors")))
  
  
  (defun isWhiteSpaceCharacter(char)
    (or (equal char #\space) (equal char #\return) (equal char #\tab) (equal char #\return)))
  
  (defun isOnlyWhiteSpace(line)
    (let* ((char (car line)))
      (if (equal char nil)
          t
          (and (isWhiteSpaceCharacter char) (isOnlyWhiteSpace (cdr line))))))
    
  
  ;Opens a BMP and executes a list of text output (error messages)
  (defun readWriteBMP(inputAsChars state)
    (let* ((breakPair (breakAtNextLine inputAsChars)))
      (let* ((line (car breakPair))
             (rest (cadr breakPair)))
        (if (or (equal line nil) (isOnlyWhiteSpace line))
            (list "Finished all files.")
            (mv-let (bmp-bytes err-opn-bmp state)
                    (binary-file->byte-list (chrs->str line) state)
                    (if err-opn-bmp
                        (list "Error Opening File")
                        (if (containsEndFile rest)
                            (let* ((remainingFiles (getRemainingFiles rest)))
                              (mv-let (tree fileErrors)
                                      (mv-let (innerTree innerErrors)
                                              (bmp->tree bmp-bytes)
                                              (runAllCommands (getFileCommands rest) innerTree innerErrors))
                                      (mv-let (error-write state) 
                                              (byte-list->binary-file (chrs->str (append line (str->chrs "_modified.bmp")))
                                                                      (tree->bmp tree)
                                                                      state)
                                              (if error-write
                                                  (append (list (string-append "Starting work on " (chrs->str line))) fileErrors (list "Error Writing File.") (readWriteBMP remainingFiles state))
                                                  (append (cons (string-append "Starting work on " (chrs->str line)) fileErrors) (readWriteBMP remainingFiles state))))));Handle checking indiviudal errors here
                            (list "No end file marker found."))))))))
  
  ;Main command which takes an input txt path, an output txt path, and the STATE. Does EVERYTHING.
  (defun executeCommandsHelper (inputTXT outputTXT state)
    (mv-let (inputAsString error-open state)
            (file->string inputTXT state)
            (if error-open
                (mv error-open state)
                (mv-let (error-close state)
                        (string-list->file outputTXT (readWriteBMP(str->chrs inputAsString) state) state)
                        (if error-close
                            (mv error-close state)
                            (mv "Complete" state))))))
  
  (defun executeCommands (inputTXT outputTXT)
    (executeCommandsHelper inputTXT outputTXT STATE))
  
  (defun runAll ()
    (executeCommands "input.txt" "log.txt"))
                
  

  
  (export IMain))

(link Main
      (MCommon MFileReader MImage MColorize MFlip MResize MCrop MThreshold MColorThreshold 
               MTrim MRotate MDecorate MGenerate MDraw MOverlay MBlur MGrayscale MHipstafy
               MFisheye MHueChange MSharpen MNegative MMask MMain))

(set-state-ok t)
(invoke Main)